import { Vec3 } from "cc";
import { DataProxy } from "../../frame/A-LIB/sources/lib.b.data";
import GlobalDC from "../../frame/common/data.global.center";
import { AudioManager } from "../../frame/component/audio-manager";
import { APP_NAME } from "../../frame/frame.config";
import { ClientAPI } from "../api/api.client";
import ServiceAPI from "../api/api.service";
import { NCAPIURI, NCBody, NCPoint, NCPointData, NCResult, NCUserPoint } from "../api/config.api.client";
import { ServiceAPIURI, ServiceResult } from "../api/config.api.service";
import { FORCE_POP } from "../config/config.game";
import GameDC from "../data/data.game.center";
import ConfManager from "./config.manager";

export default class DataManager {
	private static app_name: string;
	private static local_np: mtec.NudityPromise<'end'>;
	private static confg_np: mtec.NudityPromise<'end'>;
	private static user_np: mtec.NudityPromise<'end'>;
	private static goods_np: mtec.NudityPromise<'end'>;
	private static guide_np: mtec.NudityPromise<'end'>;

	/** 当前应用的名称 */
	public static get AppName(){
		return DataManager.app_name;
	}
	/** 本地化信息是否已经初始化 */
	public static get LocalInited(){
		return DataManager.local_np.promise;
	}
	/** 配置信息是否已经初始化 */
	public static get ConfigInited(){
		return DataManager.confg_np.promise;
	}
	/** 用户信息是否同步完成 */
	public static get UserInfoSynced(){
		return DataManager.user_np.promise;
	}
	/** 商品信息是否同步完成 */
	public static get GoodsSynced(){
		return DataManager.goods_np.promise;
	}
	public static get GuideInited(){
		return DataManager.guide_np.promise;
	}

	private static __master_remove__: number;
	public static get master_remove(){
		return DataManager.__master_remove__;
	}

	public static init(){
		DataManager.app_name = APP_NAME.game;

		GlobalDC.init(DataManager.AppName);
		GameDC.init(DataManager.AppName);

		DataManager.guide_np = new mtec.NudityPromise();

		DataManager.syncData();
		DataManager.syncGoodsInfo();

		DataManager.listening();
	}

	/** 初始化本地化信息 */
	private static syncData(){
		DataManager.local_np = new mtec.NudityPromise();
		DataManager.confg_np = new mtec.NudityPromise();
		DataManager.user_np = new mtec.NudityPromise();

		if(GameDC.User.uid){
			DataManager.local_np.resolve('end');
		}else{
			Promise.all([
				ClientAPI.Local(),
				ClientAPI.DeviceID(),
			]).then(([local, did])=>{
				GameDC.User.device_id = did;
				return ServiceAPI.userInit(ConfManager.safeCountry(local.countryCode), did);
			}).then(result=>{
				GameDC.User.uid = result.userId;
				GameDC.User.country = result.country;
				GameDC.User.language = result.language;
				DataManager.local_np.resolve('end');
			});
		}

		DataManager.LocalInited
		.then(()=>{
			Promise.all([
				ServiceAPI.countConf(GameDC.User.country),
				ServiceAPI.otherConf(GameDC.User.country),
			]).then(([coun, othr])=>{
				ConfManager.setCountryConfig(coun);
				ConfManager.setOtherConfig(othr);
				DataManager.confg_np.resolve('end');
			});

			return ServiceAPI.userInfo(GameDC.User.uid);
		}).then(result=>{
			GameDC.User.diamond = result.diamond;
			GameDC.User.drawer_lock = !result.addSpace;
			DataManager.user_np.resolve('end');
		});
	}

	private static async syncGoodsInfo(){
		DataManager.goods_np = new mtec.NudityPromise();
		await DataManager.ConfigInited;
		let goods_list = [
			ConfManager.otherConf.buffPrice.unlockDrawerGoodsId,
			ConfManager.otherConf.buffPrice.removeAdPackGoodsId,
			ConfManager.otherConf.buffPrice.superGiftGoodsId,
			ConfManager.otherConf.buffPrice.masterPassGoodsId,
		];
		goods_list.push(...ConfManager.otherConf.diamondPrice.map(el=>el.goodsId));

		for(let i = 0; i<goods_list.length; i++){
			if(!goods_list[i]){
				continue;
			}
			await DataManager.getGoodsInfo(goods_list[i]);
		}

		for(let item of ConfManager.otherConf.diamondPrice){
			let data: DiamondGoodsInfo;
			if(item.goodsId){
				data = await DataManager.getGoodsInfo(item.goodsId) as any;
			}else{
				data = {} as any;
			}
			Object.assign(data, item);

			DataManager.diamond_goods_list.push(data);
		}
		DataManager.diamond_goods_list.sort((a, b)=>a.diamond-b.diamond);

		DataManager.goods_np.resolve('end');
	}

	private static listening(){
		/** 全局按键点击音效 */
		DataProxy.follow(GlobalDC.Trigger, 'CLICK', (o, v)=>{
			AudioManager.playEffect(AudioManager.EAudio.EF_CLICK);
		}, false);
		DataProxy.follow(GameDC.User, 'master_remove', ()=>{
			DataManager.__master_remove__ = GameDC.Cache.remove_count - GameDC.User.master_remove;
		});
		DataProxy.follow(GameDC.Cache, 'remove_count', ()=>{
			DataManager.__master_remove__ = GameDC.Cache.remove_count - GameDC.User.master_remove;
		});

		if(GameDC.Status.guide_inited){
			DataManager.guide_np.resolve('end');
		}else{
			let reg = DataProxy.follow(GameDC.Status, 'guide_inited', (o, inited)=>{
				if(inited){
					DataProxy.cancel(reg);
					DataManager.guide_np.resolve('end');
				}
			}, false);
		}

		if(GameDC.User.master_time){
			let duration = GlobalDC.Status.curr_time - GameDC.User.master_time;
			if((duration / mtec.time.ONE_DAY) >= 10){
				GameDC.User.master_time = GlobalDC.Status.curr_time;
				GameDC.User.master_free_level = 0;
				GameDC.User.master_level = 0;
				GameDC.User.master_remove = GameDC.Cache.remove_count;
			}
		}else{
			GameDC.User.master_time = GlobalDC.Status.curr_time;
			GameDC.User.master_remove = GameDC.Cache.remove_count;
			GameDC.User.master_free_level = 0;
			GameDC.User.master_level = 0;
		}
	}

	public static checkForcePop(){
		let count = GameDC.Cache.remove_count;

		if(!(count>0) || count%3!=0) return void 0;

		let rp_count = Math.floor(count / 3);
		let conf = FORCE_POP.find(el=>rp_count.inRange(el.range[0], el.range[1]));

		let force_rp = (mtec.local.read<number>(DataManager.AppName+'force-rp') ?? 0) + 1;
		let force_idx = (mtec.local.read<number>(DataManager.AppName+'force-idx') ?? 0);


		if(conf.range[0] == rp_count) force_idx = 0;

		let force = force_rp >= conf.interval[force_idx];

		let type: NCBody<NCAPIURI.AD>['type'];
		if(force){
			force_idx = (force_idx + 1) % conf.interval.length;
			force_rp = 0;

			let force_count = (mtec.local.read<number>(DataManager.AppName+'force-count') ?? 0) + 1;
			if(force_count <= 5){
				type = 'full-screen';
				mtec.local.save(DataManager.AppName+'force-count', force_count);
			}else type = 'incentive';
		}

		mtec.local.save(DataManager.AppName+'force-rp', force_rp);
		mtec.local.save(DataManager.AppName+'force-idx', force_idx);

		return type;
	}

	public static cleanGameStatus(){
		GameDC.Status.remove_tile_count = 0;
		GameDC.Status.gameover = undefined;
		GameDC.Status.resurgence = 1;
		mtec.array.clear(DataManager.prop_geted_list);
		GameDC.Status.prop_stash = 0;
		GameDC.Status.prop_revoke = 0;
		GameDC.Status.prop_shuffle = 0;
	}

	/**
	 * 用户点位上报
	 * @param point - 点位标识符
	 * @param args - 点位数据
	 */
	public static userPoint
	<p extends NCUserPoint, d extends Omit<NCPointData<typeof NCPoint[p]>, 'userId'>>
	(point: p, ...args: {} extends d ? [] : [d]){
		let data = args[0] ?? {};
		Reflect.set(data, 'userId', GameDC.User.uid);
		ClientAPI.Point(NCPoint[point], data);
	}

	/**
	 * 声明周期只上报一次的点位
	 * @param point - 点位标识符
	 * @param check - 检查点位标识符
	 */
	public static oncePoint(point: NCOncePoint, check?: NCOncePoint){
		check = check ?? point;

		if(!mtec.local.read(DataManager.AppName+check)){
			DataManager.userPoint(point);
			if(point!=check){
				mtec.local.save(DataManager.AppName+point, point);
			}
		}
	}

	/**
	 * 同步钻石修改操作到服务器
	 * @param amount
	 */
	public static changeDiamond(amount: number){
		GameDC.User.diamond += amount;
		DataManager.diamond_change_list.unshift(amount);
		DataManager.runDiamondChange();
	}
	private static diamond_change_list: number[] = [];
	private static diamond_change_lock = false;
	private static async runDiamondChange(){
		if(DataManager.diamond_change_lock){
			return void 0;
		}
		DataManager.diamond_change_lock = true;

		let uid = GameDC.User.uid;
		let change_value: number;
		let res: ServiceResult<ServiceAPIURI.DiamondSync>;
		while(DataManager.diamond_change_list.length>0){
			change_value = DataManager.diamond_change_list.pop();
			res = await ServiceAPI.diamondSync(uid, change_value);
			if(!res){
				DataManager.diamond_change_list.push(change_value);
			}
		}

		if(DataManager.diamond_change_list.length>0){
			DataManager.diamond_change_lock = false;
			DataManager.runDiamondChange();
		}else{
			if(GameDC.User.diamond != res.diamond){
				GameDC.User.diamond = res.diamond;
			}
			DataManager.diamond_change_lock = false;
		}
	}

	private static float_diamond_vec = new Vec3();
	private static float_diamond_vec_ref: Vec3;
	public static get diamond_float_from(){
		return DataManager.float_diamond_vec_ref;
	}
	public static floatDiamond(num: number, world_position?: Vec3){
		if(world_position){
			DataManager.float_diamond_vec.set(world_position);
			DataManager.float_diamond_vec_ref = DataManager.float_diamond_vec;
		}else{
			DataManager.float_diamond_vec_ref = null;
		}
		GameDC.Trigger.FLOAT_DIAMOND = num;
	}

	private static goods_info_map: Map<string, NCResult<NCAPIURI.GoodsInfo>> = new Map();
	public static async getGoodsInfo(goodsId: string){
		if(!DataManager.goods_info_map.has(goodsId)){
			let res = await ClientAPI.GoodsInfo(goodsId);
			if(res){
				DataManager.goods_info_map.set(goodsId, res);
			}else{
				mtec.log.warn('获取商品信息失败[goodsId:]', '<'+goodsId+'>');
			}
		}
		return DataManager.goods_info_map.get(goodsId);
	}

	public static async buyLockGoods(){
		let status = await ClientAPI.GoodsTrade(ConfManager.otherConf.buffPrice.unlockDrawerGoodsId);
		if(status=='trade-success'){
			DataManager.userPoint('GetAddSpace', {replace: (await DataManager.getGoodsInfo(ConfManager.otherConf.buffPrice.unlockDrawerGoodsId)).price});
			let unlock = await ServiceAPI.Unlock(GameDC.User.uid);
			while(!unlock){
				unlock = await ServiceAPI.Unlock(GameDC.User.uid);
			}

			GameDC.User.drawer_lock = false;
		}
	}

	public static async buyGBGoods(type: 'blocker'|'gift'){
		let gid: string;
		if(type == 'blocker'){
			gid = ConfManager.otherConf.buffPrice.removeAdPackGoodsId;
		}else{
			gid = ConfManager.otherConf.buffPrice.superGiftGoodsId;
		}

		let status = await ClientAPI.GoodsTrade(gid);
		let success = status == 'trade-success';
		if(success){
			if(type == 'blocker'){
				GameDC.User.adblock = false;
			}else{
				GameDC.User.gift = false;
			}
		}

		return success;
	}

	public static async buyDiamond(goodId: string){
		let goods = DataManager.diamond_goods.find(el=>el.goodsId == goodId);
		let status = await ClientAPI.GoodsTrade(goodId);

		if(status == 'trade-success'){
			DataManager.changeDiamond(goods.diamond);
			let level = parseInt(goods.goodsId.replace('com.match.pay', ''));
			DataManager.userPoint('GetLevel'+level+'Success' as any, {replace: goods.price});
		}
		return {status, diamond: goods.diamond};
	}

	public static getMasterReap(){
		let map: Map<'free'|'master'|'stash'|'revoke'|'shuffle', number> = new Map();
		(<const>['free', 'master', 'stash', 'revoke', 'shuffle']).forEach(el=>map.set(el, 0));

		let data_list = ConfManager.masterList;
		let reap_max_idx = -1;
		while((data_list[reap_max_idx+1].remove_total<=DataManager.master_remove) && (reap_max_idx<data_list.length)){
			reap_max_idx++;
		}

		let reap_data = data_list[reap_max_idx];
		let last_free = GameDC.User.master_free_level;
		let last_master = GameDC.User.master_level;
		let master = GameDC.User.master;

		if(reap_data){
			for(let temp of data_list){
				if(temp.level > reap_data.level){
					break;
				}

				if(temp.level>last_free && temp.level<=reap_data.level){
					map.set('free', map.get('free')+temp.commonDiamondCount);
					last_free = temp.level;
				}

				if(master && temp.level>last_master && temp.level<=reap_data.level){
					let type = ['master', 'stash', 'revoke', 'shuffle'][temp.specialPassport.type-1] as any;
					map.set(type, map.get(type)+temp.specialPassport.count);
					last_master = temp.level;
				}
			}
		}

		let clean_list: any[] = [];
		map.forEach((v, k)=>v>0 ? void 0 : clean_list.push(k));
		clean_list.forEach(k=>map.delete(k));

		let diamond = 0;
		(<const>['free', 'master']).forEach(t=>map.has(t) ? diamond+=map.get(t) : void 0);

		return { map, free_level: last_free, master_level: last_master, diamond};
	}

	public static reapMaster(){
		let reap_info = DataManager.getMasterReap();
		GameDC.User.master_free_level = reap_info.free_level;
		GameDC.User.master_level = reap_info.master_level;

		if(reap_info.diamond>0){
			DataManager.changeDiamond(reap_info.diamond);
		}

		if(reap_info.map.has('stash')){
			GameDC.Status.prop_stash += reap_info.map.get('stash');
		}
		if(reap_info.map.has('revoke')){
			GameDC.Status.prop_revoke += reap_info.map.get('revoke');
		}
		if(reap_info.map.has('shuffle')){
			GameDC.Status.prop_shuffle += reap_info.map.get('shuffle');
		}

		return reap_info;
	}

	private static prop_geted_list:Array<'stash'|'revoke'|'shuffle'> = [];
	public static get prop_get(){
		return this.prop_geted_list;
	}

	private static diamond_goods_list: DiamondGoodsInfo[] = [];
	public static get diamond_goods(){
		return DataManager.diamond_goods_list;
	}

	public static async activeMasterPass(){
		let status = await ClientAPI.GoodsTrade(ConfManager.otherConf.buffPrice.masterPassGoodsId);
		if(status == 'trade-success'){
			GameDC.User.master = true;
		}
	}
}

type NCOncePoint = 'GameInitFinish'|'GameStart'|'GameInitStart';

type DiamondGoodsInfo = mtec.Fusion<NCResult<NCAPIURI.GoodsInfo>, typeof ConfManager.otherConf.diamondPrice[number]>;
